<!-- 定高虚拟列表 -->
<template>
  <!-- 虚拟dom -->
  <div class="virtualList-wrap" ref="wrapRef" :style="{ height: height + 'px', border: '1px solid red' }" @scroll="onScroll">
    <!-- 真实dom -->
    <div class="virtualList-list" :style="{ height: itemHeight * list.length + 'px', border: '1px solid green' }">
      <!-- 可视区域 -->
      <div class="visiable-content" style="border: '2px solid blue'">
        <!-- items -->
        <div v-for="(item, index) in renderList" :key="index" :style="item.style">item-{{ item.index }}</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import type { StyleValue } from 'vue'

const wrapRef = ref<HTMLElement>() // 节点
const list = Array.from(new Array(1000), (_, i) => i) // 数据
const height = 400
const itemHeight = 70
const visibleCount = Math.ceil(height / itemHeight) // 可视数量
const bufferSize = 1 // 缓冲区
const startIndex = ref(0) // 开始下标
const originStartIndex = ref(0) // 真实开始下标
const endIndex = ref(Math.min(startIndex.value + visibleCount + bufferSize, list.length)) // 结束下标
const renderList = computed(() => {
  // 渲染列表
  const res = []
  for (let i = startIndex.value; i <= endIndex.value; i++) {
    res.push({
      index: i,
      style: {
        height: itemHeight + 'px',
        lineHeight: itemHeight + 'px',
        position: 'absolute',
        top: i * itemHeight + 'px',
        left: 0,
        right: 0,
        borderBottom: '1px solid #000',
        width: '100%',
      } as StyleValue,
    })
  }
  return res
})

console.log('渲染数组',renderList);


const onScroll = (e: Event) => {
  if (e.target == wrapRef.value) {
    const { scrollTop } = e.target as HTMLElement
    const currentIndex = Math.floor(scrollTop / itemHeight)
    if (startIndex.value !== currentIndex) {
      originStartIndex.value = currentIndex // 真实开始下标
      startIndex.value = Math.max(originStartIndex.value - bufferSize, 0) // 头部缓冲计算
      endIndex.value = Math.min(originStartIndex.value + visibleCount + bufferSize, list.length - 1) // 尾部缓冲计算
    }
  }
}
</script>

<style lang="scss" scoped>
.virtualList-wrap {
  overflow-y: auto;
}
.virtualList-list {
  position: relative;
}
</style>
